package chartCraft.graphDesk;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JLayeredPane;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

import chartCraft.ChartCraftController;
import chartCraft.model.LocationModel;
import chartCraft.model.Model;
import chartCraft.model.Model.NotifyingMessage;
import chartCraft.model.TopModel;

public class CanvasPane extends JLayeredPane implements Observer {

	private static final long serialVersionUID = 1L;
	private final static Integer CHART_DEPTH = new Integer(1);
	private final static Integer BACKGROUND_DEPTH = new Integer(0);
	
	private TopModel mTopModel;
	
	private final ChartCraftController mChartCraftController;
	
	private final DraggableBackground mBackground;
	private Map<Model, DraggableItem> mChartOnCanvas= new HashMap<Model, DraggableItem>();

	public CanvasPane(ChartCraftController chartCraftController, TopModel topModel) {
		mChartCraftController = chartCraftController;
		mBackground = new DraggableBackground(chartCraftController, topModel);

		setModel(topModel);
		addMouseListener(canvasMouseEventListener);
		
		updateDraggableBackground();
	}
	
	public void updateDraggableBackground() {
		boolean showingBackground = false;
		for (Component c: getComponents()) {
			showingBackground |= (c == mBackground);
		}
		
		boolean shouldShowBackground;
		
		if (mTopModel.getBackgroundImage() != null && !mTopModel.isImagieHiding()) {
			shouldShowBackground = true;
		} else {
			shouldShowBackground = false;
		}
				
		if (shouldShowBackground && !showingBackground) {
			add(mBackground, BACKGROUND_DEPTH);
			
		} else if (!shouldShowBackground && showingBackground) {

			delete(mBackground);

		}
		
	}

	private void insert(DraggableItem item) {
		add(item, CHART_DEPTH);
				
		/** Place the item on the canvas */
		Point location = ((LocationModel) item.getModel()).getCanvasLocation();
		Dimension itemSize = ((LocationModel) item.getModel()).getSize();
		item.setBounds(location.x, location.y, itemSize.width, itemSize.height);
		repaint();   
		item.invalidate();   
		revalidate();
		
		mChartOnCanvas.put(item.getModel(), item);
	}
	
	private void delete(DraggableItem item) {
		remove(item);
		
		/** Place the item on the canvas */
		repaint();   
		revalidate();
		
		mChartOnCanvas.remove(item.getModel());
	}
	
	private void updateLayerDepth() {
		final ArrayList<LocationModel> layerDepth = mTopModel.getLayerDepth();
		for (int depth = 0; depth < layerDepth.size(); depth++) {
			final Model modelAtDepth = layerDepth.get(depth);
			final DraggableItem itemAtDepth = mChartOnCanvas.get(modelAtDepth);
			this.setLayer(itemAtDepth, CHART_DEPTH, depth);
		}
	}

	/**
	 * This listener is responsible for 
	 * a) de-selection of draggableItem on canvas
	 * @author ctldenon
	 *
	 */
	MouseInputListener canvasMouseEventListener = new MouseInputAdapter() {
		public void mousePressed(MouseEvent me){
			mChartCraftController.deselectAllLocations();
		}
	};

	@Override
	public void update(Observable arg0, Object arg1) {
		if (arg0 == mTopModel) {
			NotifyingMessage msg = (NotifyingMessage) arg1;
			String action = msg.getAction();
			
			if (action == TopModel.INSERT_NEW_LOCATION_MODEL) {
				LocationModel locationModel = (LocationModel) msg.getObject();
				DraggableChart grapgItem = new DraggableChart(mChartCraftController, locationModel, mTopModel);
				insert(grapgItem);
			} else if (action == TopModel.DELETE_LOCATION_MODEL) {
				LocationModel locationModel = (LocationModel) msg.getObject();
				DraggableItem grapgItem = mChartOnCanvas.get(locationModel);
				delete(grapgItem);
			} else if (action == TopModel.BACKGROUND_DATA_UPDATED) {
				updateDraggableBackground();
			} else if (action == TopModel.BACKGROUND_HIDDING_CHANGED) {
				updateDraggableBackground();
			} else if (action == TopModel.LAYER_DEPTH_CHANGED) {
				updateLayerDepth();
			}
		}
	}

	public void setModel(TopModel topModel) {
		mTopModel = topModel;
		mTopModel.addGraphViewObserver(this);
		
		reloadCurrentState();
	}

	private void reloadCurrentState() {
		removeAll();
		
		for(LocationModel locationModel: mTopModel.getAllLocationModels()) {
			DraggableChart grapgItem = new DraggableChart(mChartCraftController, locationModel, mTopModel);
			insert(grapgItem);
		}
		
		updateLayerDepth();
		updateDraggableBackground();
	}
}

